package com.szwistar.common.comm.getui;

import com.szwistar.common.comm.HttpUtils;
import com.szwistar.common.comm.SmsService;
import com.szwistar.common.datastruct.JsonMapper;
import com.szwistar.common.datastruct.JsonObject;
import com.szwistar.common.datastruct.Utils;

import org.apache.commons.codec.digest.DigestUtils;
import org.apache.log4j.PropertyConfigurator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.HashMap;
import java.util.Map;

/**
 * 个推短信发送服务
 * @author 张勇
 */
public class GetuiSmsService implements SmsService {
	protected static final Logger LOG = LoggerFactory.getLogger(GetuiSmsService.class);

	// AccessToken 有效期(ms)
	public static final long TokenTTL = 2*3600*1000;

	/**
	 * 个推短信API接口地址：获取调用所需的 access_token<br>
	 * <a href="http://docs.getui.com/sms/interface/#doc-title-3">鉴权接口</a>
	 */
	public static final String URLGetAccessToken = "https://openapi-smsp.getui.com/v1/sps/auth_sign";

	/**
	 * 个推短信API接口地址：短信群发接口<br>
	 * <a href="http://docs.getui.com/sms/interface/#doc-title-4">短信群发接口</a>
	 */
	public static final String URLPushSmsList = "https://openapi-smsp.getui.com/v1/sps/push_sms_list";

	/**
	 * 短信回执通知回调URL
	 */
	public static final String URLSmsNotify = "https://ehome.szwistar.com/cloudBridge/getuiSmsNotify";

	// appId <--> 服务 映射表
	static Map<String, GetuiSmsService> appServiceMap = new HashMap<String, GetuiSmsService>();

	// 获取 APP 服务
	public static SmsService getAppService(String appId) {
		if(appId == null) { return null; }
		return appServiceMap.get(appId);
	}

	// 应用显示名称
	protected String title;
	// appId
	protected String appId;
	// appKey
	protected String appKey;
	// appSecret
	protected String appSecret;
	// masterSecret
	protected String masterSecret;
	// 个推服务器授权token
	protected String accessToken;
	// 授权时间
	protected long expire = 0;
	// 默认的验证码发送模板
	protected String defaultVerifyTemplateId;

	public String getTitle() { return title; }
	public String getAppId() { return appId; }
	public String getAppKey() { return appKey; }
	public String getAppSecret() { return appSecret; }
	public String getMasterSecret() { return masterSecret; }

	public GetuiSmsService(String title, String appId, String appKey, String appSecret, String masterSecret, String defaultVerifyTemplateId) {
		super();
		this.title = title;
		this.appId = appId;
		this.appKey = appKey;
		this.appSecret = appSecret;
		this.masterSecret = masterSecret;
		this.defaultVerifyTemplateId = defaultVerifyTemplateId;
		appServiceMap.put(appId, this);
	}

	/**
	 * 获取 API 调用所需的 access_token
	 * @return true: accessToken 已更新
	 * 		   false: accessToken 未更新
	 * 		   null: 获取失败，状态未知
	 */
	protected Boolean getAccessToken() {
		try {
			long timestamp = System.currentTimeMillis();
			String sign = DigestUtils.sha256Hex(String.format("%s%d%s", appKey, timestamp, masterSecret));
			JsonObject params = new JsonObject();
			params.put("sign", sign);
			params.put("timestamp", timestamp);
			params.put("appId", appId);
			String json = HttpUtils.request(URLGetAccessToken, null, params.toJson());
			LOG.debug("getAccessToken(): {}", json);

			JsonObject res = JsonObject.parse(json);
			res = res.getJsonObject("data", null);
			accessToken = res.getStr("authToken", null);
			if(Utils.isEmpty(accessToken)) { return false; }

			expire = timestamp + TokenTTL;
			return true;
		} catch (Exception e) {
			accessToken = null;
			LOG.error("{} getAccessToken() 失败！{}", this, e.getCause());
			return null;
		}
	}

	/**
	 * 检查accessToken是否有效，如果无效则重新获取
	 * @return 是否成功
	 */
	protected Boolean validateAccessToken() {
		if(Utils.isNotEmpty(accessToken) && System.currentTimeMillis() < expire) {
			return true;
		}

		// 重新获取
		accessToken = null;
		getAccessToken();

		return (accessToken != null);
	}

	@Override
	public boolean send(String[] tels, String templateId, Map<String, Object> smsParam) {
		if(!validateAccessToken()) { return false; }

		// 把电话号码用 MD5 加密
		for(int i=0; i<tels.length; i++) {
			tels[i] = DigestUtils.md5Hex(tels[i]);
		}

		try {
			JsonObject params = new JsonObject();
			params.put("appId", appId);
			params.put("authToken", accessToken);
			params.put("smsTemplateId", templateId);
			params.put("smsParam", JsonMapper.toJson(smsParam));
			params.put("recNum", JsonMapper.toJson(tels));
			//params.put("notifyUrl", URLSmsNotify);

			LOG.debug("向 {} 发送短信：{}", params.get("recNum"), params.get("smsParam"));
			String ret = HttpUtils.request(URLPushSmsList, null, params.toJson());
			LOG.debug("发送短信返回：{}", ret);

			JsonObject res = JsonObject.parse(ret);
			return (res.getInt("result", -1) == 20000);
		} catch (Exception e) {
			accessToken = null;
			LOG.error("{} send() 失败！{}", this, e.getCause());
			return false;
		}
	}

	@Override
	public boolean send(String targetTel, String templateId, Map<String, Object> smsParam) {
		String tels[] = new String[1];
		tels[0] = targetTel;
		return send(tels, templateId, smsParam);
	}

	@Override
	public String sendVerify(String targetTel, String templateId) {
		String code = Integer.toString(Utils.random(100000, 999999));
		boolean ret = send(targetTel, templateId, new JsonObject("code", code, "time", "15分钟"));
		return ret ? code : null;
	}

	@Override
	public String sendVerify(String targetTel) {
		return sendVerify(targetTel, defaultVerifyTemplateId);
	}

	// for test only
	public static void main(String[] args) throws Exception {
		PropertyConfigurator.configure("log4j.properties");

		GetuiSmsService svc = new GetuiSmsService(
				"智辰智能家居",
				"SL9bvOiu6V9kajDoz48PJA",
				"zzyfoCf2qS6WZHt6uAlHI2",
				"IRQeJ02fdM6rkdNKbCkHy5",
				"rNxHdLQ45X768euohi9MF5",
				"201903121022141568");

		svc.getAccessToken();
		//svc.validateAccessToken();

		Map<String, Object> smsParam = new HashMap<String, Object>();
		smsParam.put("code", String.format("%06d", (int)(Math.random()*1000000)));
		smsParam.put("time", "15分钟");
		String tels[] = {"18612345678", "13312345678"};
		svc.send(tels, "201903121022141568", smsParam);
	}
}
